#ifndef U2VECTOR2_H
#define U2VECTOR2_H

#include "U2PreRequest.h"
#include "U2Math.h"


U2EG_NAMESPACE_BEGIN


class _U2Share U2Vector2
{
public:
    u2real x, y;

public:
    inline U2Vector2()
    {
    }

    inline U2Vector2(const u2real fX, const u2real fY )
        : x( fX ), y( fY )
    {
    }

    inline explicit U2Vector2( const u2real scaler )
        : x( scaler), y( scaler )
    {
    }

    inline explicit U2Vector2( const u2real afCoordinate[2] )
        : x( afCoordinate[0] ),
          y( afCoordinate[1] )
    {
    }

    inline explicit U2Vector2( const int afCoordinate[2] )
    {
        x = (u2real)afCoordinate[0];
        y = (u2real)afCoordinate[1];
    }

    inline explicit U2Vector2( u2real* const r )
        : x( r[0] ), y( r[1] )
    {
    }

	/** Exchange the contents of this vector with another. 
	*/
	inline void swap(U2Vector2& other)
	{
		std::swap(x, other.x);
		std::swap(y, other.y);
	}

	inline u2real operator [] ( const size_t i ) const
    {
        assert( i < 2 );

        return *(&x+i);
    }

	inline u2real& operator [] ( const size_t i )
    {
        assert( i < 2 );

        return *(&x+i);
    }

	/// Pointer accessor for direct copying
	inline u2real* ptr()
	{
		return &x;
	}
	/// Pointer accessor for direct copying
	inline const u2real* ptr() const
	{
		return &x;
	}

    /** Assigns the value of the other vector.
    @param
        rkVector The other vector
    */
    inline U2Vector2& operator = ( const U2Vector2& rkVector )
    {
        x = rkVector.x;
        y = rkVector.y;

        return *this;
    }

	inline U2Vector2& operator = ( const u2real fScalar)
	{
		x = fScalar;
		y = fScalar;

		return *this;
	}

    inline bool operator == ( const U2Vector2& rkVector ) const
    {
        return ( x == rkVector.x && y == rkVector.y );
    }

    inline bool operator != ( const U2Vector2& rkVector ) const
    {
        return ( x != rkVector.x || y != rkVector.y  );
    }

    // arithmetic operations
    inline U2Vector2 operator + ( const U2Vector2& rkVector ) const
    {
        return U2Vector2(
            x + rkVector.x,
            y + rkVector.y);
    }

    inline U2Vector2 operator - ( const U2Vector2& rkVector ) const
    {
        return U2Vector2(
            x - rkVector.x,
            y - rkVector.y);
    }

    inline U2Vector2 operator * ( const u2real fScalar ) const
    {
        return U2Vector2(
            x * fScalar,
            y * fScalar);
    }

    inline U2Vector2 operator * ( const U2Vector2& rhs) const
    {
        return U2Vector2(
            x * rhs.x,
            y * rhs.y);
    }

    inline U2Vector2 operator / ( const u2real fScalar ) const
    {
        assert( fScalar != 0.0 );

        u2real fInv = 1.0f / fScalar;

        return U2Vector2(
            x * fInv,
            y * fInv);
    }

    inline U2Vector2 operator / ( const U2Vector2& rhs) const
    {
        return U2Vector2(
            x / rhs.x,
            y / rhs.y);
    }

    inline const U2Vector2& operator + () const
    {
        return *this;
    }

    inline U2Vector2 operator - () const
    {
        return U2Vector2(-x, -y);
    }

    // overloaded operators to help U2Vector2
    inline friend U2Vector2 operator * ( const u2real fScalar, const U2Vector2& rkVector )
    {
        return U2Vector2(
            fScalar * rkVector.x,
            fScalar * rkVector.y);
    }

    inline friend U2Vector2 operator / ( const u2real fScalar, const U2Vector2& rkVector )
    {
        return U2Vector2(
            fScalar / rkVector.x,
            fScalar / rkVector.y);
    }

    inline friend U2Vector2 operator + (const U2Vector2& lhs, const u2real rhs)
    {
        return U2Vector2(
            lhs.x + rhs,
            lhs.y + rhs);
    }

    inline friend U2Vector2 operator + (const u2real lhs, const U2Vector2& rhs)
    {
        return U2Vector2(
            lhs + rhs.x,
            lhs + rhs.y);
    }

    inline friend U2Vector2 operator - (const U2Vector2& lhs, const u2real rhs)
    {
        return U2Vector2(
            lhs.x - rhs,
            lhs.y - rhs);
    }

    inline friend U2Vector2 operator - (const u2real lhs, const U2Vector2& rhs)
    {
        return U2Vector2(
            lhs - rhs.x,
            lhs - rhs.y);
    }

    // arithmetic updates
    inline U2Vector2& operator += ( const U2Vector2& rkVector )
    {
        x += rkVector.x;
        y += rkVector.y;

        return *this;
    }

    inline U2Vector2& operator += ( const u2real fScaler )
    {
        x += fScaler;
        y += fScaler;

        return *this;
    }

    inline U2Vector2& operator -= ( const U2Vector2& rkVector )
    {
        x -= rkVector.x;
        y -= rkVector.y;

        return *this;
    }

    inline U2Vector2& operator -= ( const u2real fScaler )
    {
        x -= fScaler;
        y -= fScaler;

        return *this;
    }

    inline U2Vector2& operator *= ( const u2real fScalar )
    {
        x *= fScalar;
        y *= fScalar;

        return *this;
    }

    inline U2Vector2& operator *= ( const U2Vector2& rkVector )
    {
        x *= rkVector.x;
        y *= rkVector.y;

        return *this;
    }

    inline U2Vector2& operator /= ( const u2real fScalar )
    {
        assert( fScalar != 0.0 );

        u2real fInv = 1.0f / fScalar;

        x *= fInv;
        y *= fInv;

        return *this;
    }

    inline U2Vector2& operator /= ( const U2Vector2& rkVector )
    {
        x /= rkVector.x;
        y /= rkVector.y;

        return *this;
    }

    /** Returns the length (magnitude) of the vector.
    @warning
        This operation requires a square root and is expensive in
        terms of CPU operations. If you don't need to know the exact
        length (e.g. for just comparing lengths) use squaredLength()
        instead.
    */
    inline u2real length () const
    {
        return U2Math::Sqrt( x * x + y * y );
    }

    /** Returns the square of the length(magnitude) of the vector.
    @remarks
        This  method is for efficiency - calculating the actual
        length of a vector requires a square root, which is expensive
        in terms of the operations required. This method returns the
        square of the length of the vector, i.e. the same as the
        length but before the square root is taken. Use this if you
        want to find the longest / shortest vector without incurring
        the square root.
    */
    inline u2real squaredLength () const
    {
        return x * x + y * y;
    }

    /** Returns the distance to another vector.
    @warning
        This operation requires a square root and is expensive in
        terms of CPU operations. If you don't need to know the exact
        distance (e.g. for just comparing distances) use squaredDistance()
        instead.
    */
    inline u2real distance(const U2Vector2& rhs) const
    {
        return (*this - rhs).length();
    }

    /** Returns the square of the distance to another vector.
    @remarks
        This method is for efficiency - calculating the actual
        distance to another vector requires a square root, which is
        expensive in terms of the operations required. This method
        returns the square of the distance to another vector, i.e.
        the same as the distance but before the square root is taken.
        Use this if you want to find the longest / shortest distance
        without incurring the square root.
    */
    inline u2real squaredDistance(const U2Vector2& rhs) const
    {
        return (*this - rhs).squaredLength();
    }

    /** Calculates the dot (scalar) product of this vector with another.
    @remarks
        The dot product can be used to calculate the angle between 2
        vectors. If both are unit vectors, the dot product is the
        cosine of the angle; otherwise the dot product must be
        divided by the product of the lengths of both vectors to get
        the cosine of the angle. This result can further be used to
        calculate the distance of a point from a plane.
    @param
        vec Vector with which to calculate the dot product (together
        with this one).
    @returns
        A float representing the dot product value.
    */
    inline u2real dotProduct(const U2Vector2& vec) const
    {
        return x * vec.x + y * vec.y;
    }

    /** Normalises the vector.
    @remarks
        This method normalises the vector such that it's
        length / magnitude is 1. The result is called a unit vector.
    @note
        This function will not crash for zero-sized vectors, but there
        will be no changes made to their components.
    @returns The previous length of the vector.
    */

    inline u2real normalise()
    {
        u2real fLength = U2Math::Sqrt( x * x + y * y);

        // Will also work for zero-sized vectors, but will change nothing
		// We're not using epsilons because we don't need to.
        // Read http://www.ogre3d.org/forums/viewtopic.php?f=4&t=61259
        if ( fLength > u2real(0.0f) )
        {
            u2real fInvLength = 1.0f / fLength;
            x *= fInvLength;
            y *= fInvLength;
        }

        return fLength;
    }

    /** Returns a vector at a point half way between this and the passed
        in vector.
    */
    inline U2Vector2 midPoint( const U2Vector2& vec ) const
    {
        return U2Vector2(
            ( x + vec.x ) * 0.5f,
            ( y + vec.y ) * 0.5f );
    }

    /** Returns true if the vector's scalar components are all greater
        that the ones of the vector it is compared against.
    */
    inline bool operator < ( const U2Vector2& rhs ) const
    {
        if( x < rhs.x && y < rhs.y )
            return true;
        return false;
    }

    /** Returns true if the vector's scalar components are all smaller
        that the ones of the vector it is compared against.
    */
    inline bool operator > ( const U2Vector2& rhs ) const
    {
        if( x > rhs.x && y > rhs.y )
            return true;
        return false;
    }

    /** Sets this vector's components to the minimum of its own and the
        ones of the passed in vector.
    @remarks
        'Minimum' in this case means the combination of the lowest
        value of x, y and z from both vectors. Lowest is taken just
        numerically, not magnitude, so -1 < 0.
    */
    inline void makeFloor( const U2Vector2& cmp )
    {
        if( cmp.x < x ) x = cmp.x;
        if( cmp.y < y ) y = cmp.y;
    }

    /** Sets this vector's components to the maximum of its own and the
        ones of the passed in vector.
    @remarks
        'Maximum' in this case means the combination of the highest
        value of x, y and z from both vectors. Highest is taken just
        numerically, not magnitude, so 1 > -3.
    */
    inline void makeCeil( const U2Vector2& cmp )
    {
        if( cmp.x > x ) x = cmp.x;
        if( cmp.y > y ) y = cmp.y;
    }

    /** Generates a vector perpendicular to this vector (eg an 'up' vector).
    @remarks
        This method will return a vector which is perpendicular to this
        vector. There are an infinite number of possibilities but this
        method will guarantee to generate one of them. If you need more
        control you should use the Quaternion class.
    */
    inline U2Vector2 perpendicular(void) const
    {
        return U2Vector2 (-y, x);
    }

    /** Calculates the 2 dimensional cross-product of 2 vectors, which results
		in a single floating point value which is 2 times the area of the triangle.
    */
    inline u2real crossProduct( const U2Vector2& rkVector ) const
    {
        return x * rkVector.y - y * rkVector.x;
    }

    /** Generates a new random vector which deviates from this vector by a
        given angle in a random direction.
    @remarks
        This method assumes that the random number generator has already
        been seeded appropriately.
    @param
        angle The angle at which to deviate in radians
    @param
        up Any vector perpendicular to this one (which could generated
        by cross-product of this vector and any other non-colinear
        vector). If you choose not to provide this the function will
        derive one on it's own, however if you provide one yourself the
        function will be faster (this allows you to reuse up vectors if
        you call this method more than once)
    @returns
        A random vector which deviates from this vector by angle. This
        vector will not be normalised, normalise it if you wish
        afterwards.
    */
    inline U2Vector2 randomDeviant(u2real angle) const
    {

        angle *=  U2Math::UnitRandom() * U2Math::TWO_PI;
        u2real cosa = cos(angle);
        u2real sina = sin(angle);
        return  U2Vector2(cosa * x - sina * y,
                        sina * x + cosa * y);
    }

    /** Returns true if this vector is zero length. */
    inline bool isZeroLength(void) const
    {
        u2real sqlen = (x * x) + (y * y);
        return (sqlen < (1e-06 * 1e-06));

    }

    /** As normalise, except that this vector is unaffected and the
        normalised vector is returned as a copy. */
    inline U2Vector2 normalisedCopy(void) const
    {
        U2Vector2 ret = *this;
        ret.normalise();
        return ret;
    }

    /** Calculates a reflection vector to the plane with the given normal .
    @remarks NB assumes 'this' is pointing AWAY FROM the plane, invert if it is not.
    */
    inline U2Vector2 reflect(const U2Vector2& normal) const
    {
        return U2Vector2( *this - ( 2 * this->dotProduct(normal) * normal ) );
    }

	/// Check whether this vector contains valid values
	inline bool isNaN() const
	{
		return U2Math::isNaN(x) || U2Math::isNaN(y);
	}

	/**	 Gets the angle between 2 vectors.
	@remarks
		Vectors do not have to be unit-length but must represent directions.
	*/
	inline Radian angleBetween(const U2Vector2& other) const
	{		
		u2real lenProduct = length() * other.length();
		// Divide by zero check
		if(lenProduct < 1e-6f)
			lenProduct = 1e-6f;
	
		u2real f = dotProduct(other) / lenProduct;

		f = U2Math::Clamp(f, (u2real)-1.0, (u2real)1.0);
		return U2Math::ACos(f);
	}

	/**	 Gets the oriented angle between 2 vectors.
	@remarks
		Vectors do not have to be unit-length but must represent directions.
		The angle is comprised between 0 and 2 PI.
	*/
	inline Radian angleTo(const U2Vector2& other) const
	{
		Radian angle = angleBetween(other);
	
		if (crossProduct(other)<0)			
			angle = (Radian)U2Math::TWO_PI - angle;		

		return angle;
	}


public:
    // special points
    static const U2Vector2 ZERO;
    static const U2Vector2 UNIT_X;
    static const U2Vector2 UNIT_Y;
    static const U2Vector2 NEGATIVE_UNIT_X;
    static const U2Vector2 NEGATIVE_UNIT_Y;
    static const U2Vector2 UNIT_SCALE;

public:
    /** Function for writing to a stream.
    */
    inline _U2Share friend std::ostream& operator <<
        ( std::ostream& o, const U2Vector2& v )
    {
        o << "U2Vector2(" << v.x << ", " << v.y <<  ")";
        return o;
    }
};


U2EG_NAMESPACE_END


#endif // U2VECTOR2_H